﻿using DreamSpace.Net.ScriptEngines;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using V8.Net;

namespace DreamSpace.Net
{
    public partial interface IServerHost<out TScriptEngine>
    {
        /// <summary>
        /// Verifies that the specified module is installed, and if so, returns a reference to the type (example: var http = Require("DreamSpace.NET", "HTTPServer") [note: same as 'Require("HTTPServer")']).
        /// It is of course also possible to simply use the 'typeof' statement to check for a module's existence (i.e. typeof(DreamSpace.NET.HTTPServer)).
        /// This method simply provides a more convenient way to do so, and returns a reference to the type.
        /// </summary>
        /// <param name="moduleNamespace">The name space the module exists in (case sensitive). If this is null or empty, then the namespace defaults to "DreamSpace.NET". (see 'FlattenModuleNamespace(...)')</param>
        /// <param name="moduleName">The module type (case sensitive).</param>
        /// <param name="optional">If true, then no error is generated if the type doesn't exist, and null is simply returned.</param>
        Type Using(string moduleNamespace, string moduleName, bool optional = false);

        /// <summary>
        /// Verifies that the specified module is installed, and if so, returns a reference to the type (example: var http = Require("DreamSpace.NET", "HTTPServer") [note: same as 'Require("HTTPServer")']).
        /// It is of course also possible to simply use the 'typeof' statement to check for a module's existence (i.e. typeof(DreamSpace.NET.HTTPServer)).
        /// This method simply provides a more convenient way to do so, and returns a reference to the type.
        /// </summary>
        /// <param name="moduleName">The module type (case sensitive).  The namespace defaults to "DreamSpace.NET". (see 'FlattenModuleNamespace(...))</param>
        /// <param name="optional">If true, then no error is generated if the type doesn't exist, and null is simply returned.</param>
        Type Using(string moduleName, bool optional = false);

        /// <summary>
        /// Imports a .NET type into the scripting environment.
        /// <para>For example, if you call 'Import("System.DateTime")', then the type "DateTime" will be available from the global scope. 
        /// Once imported, you can create the type via the 'new' operator like so: "var dt = new DateTime();".</para>
        /// <para>Note: The full namespace name and type name is required.  If a type is not found, an exception will be thrown.</para>
        /// </summary>
        void Import(string fullTypePath);

        /// <summary>
        /// Similar to the commonly known 'eval()' JavaScript function, and simply executes the given text as JavaScript code within the current environment, then returns the result.
        /// <para>How is this different from 'Execute()'? Well, in VBScript, the '=' symbol has two meanings: equality and assignment - so,
        /// which one to chose depends on the context. If this method is called, the context of "equality" would win.
        /// In JavaScript, however, this is not relevant.</para>
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        IBasicHandle Eval(string input);

        /// <summary>
        /// Executes the given string as JavaScript within the current environment and returns the result.
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        /// <param name="script">The script to execute.</param>
        /// <param name="sourceName">The source of the script (such as a file name).</param>
        /// <param name="throwOnError">If true, any script errors should be thrown instead of returned.</param>
        IBasicHandle Execute(string script, string sourceName = null, bool throwOnError = false);

        /// <summary>
        /// Loads and executes the given script file as JavaScript within the current environment and returns the result.
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        /// <param name="script">The script to execute.</param>
        /// <param name="sourceName">The source of the script (such as a file name).</param>
        /// <param name="throwOnError">If true, any script errors should be thrown instead of returned.</param>
        IBasicHandle LoadScript(string script, string sourceName = null, bool throwOnError = false);
 }

    public partial class ServerHost<TScriptEngine> // This class part holds the JavaScript environment function mappings.
    {
        /// <summary>
        /// Verifies that the specified module is installed, and if so, returns a reference to the type (example: var http = Require("DreamSpace.NET", "HTTPServer") [note: same as 'Require("HTTPServer")']).
        /// It is of course also possible to simply use the 'typeof' statement to check for a module's existence (i.e. typeof(DreamSpace.NET.HTTPServer)).
        /// This method simply provides a more convenient way to do so, and returns a reference to the type.
        /// </summary>
        /// <param name="moduleNamespace">The name space the module exists in (case sensitive). If this is null or empty, then the namespace defaults to "DreamSpace.NET". (see 'FlattenModuleNamespace(...)')</param>
        /// <param name="moduleName">The module type (case sensitive).</param>
        /// <param name="optional">If true, then no error is generated if the type doesn't exist, and null is simply returned.</param>
        [ScriptMember("using")]
        public Type Using(string moduleNamespace, string moduleName, bool optional = false)
        {
            return null;
        }

        /// <summary>
        /// Verifies that the specified module is installed, and if so, returns a reference to the type (example: var http = Require("DreamSpace.NET", "HTTPServer") [note: same as 'Require("HTTPServer")']).
        /// It is of course also possible to simply use the 'typeof' statement to check for a module's existence (i.e. typeof(DreamSpace.NET.HTTPServer)).
        /// This method simply provides a more convenient way to do so, and returns a reference to the type.
        /// </summary>
        /// <param name="moduleName">The module type (case sensitive).  The namespace defaults to "DreamSpace.NET". (see 'FlattenModuleNamespace(...))</param>
        /// <param name="optional">If true, then no error is generated if the type doesn't exist, and null is simply returned.</param>
        [ScriptMember("using")]
        public Type Using(string moduleName, bool optional = false)
        {
            return null;
        }

        /// <summary>
        /// Imports a .NET type into the scripting environment.
        /// <para>For example, if you call 'Import("System.DateTime")', then the type "DateTime" will be available from the global scope. 
        /// Once imported, you can create the type via the 'new' operator like so: "var dt = new DateTime();".</para>
        /// <para>Note: The full namespace name and type name is required.  If a type is not found, an exception will be thrown.</para>
        /// </summary>
        [ScriptMember("importType")]
        public void Import(string fullTypePath)
        {
            fullTypePath = fullTypePath.Trim();
            if (!fullTypePath.IsNullOrWhiteSpace())
                lock (ScriptEngine)
                {
                    ScriptEngine.DefineGlobalType(Type.GetType(fullTypePath), fullTypePath.Split('.').Last().Trim(), ScriptMemberSecurity.ReadOnly);
                }
        }

        /// <summary>
        /// Similar to the commonly known 'eval()' JavaScript function, and simply executes the given text as JavaScript code within the current environment, then returns the result.
        /// <para>How is this different from 'Execute()'? Well, in VBScript, the '=' symbol has two meanings: equality and assignment - so,
        /// which one to chose depends on the context. If this method is called, the context of "equality" would win.
        /// In JavaScript, however, this is not relevant.</para>
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        [ScriptMember("eval")]
        public IBasicHandle Eval(string input)
        {
            lock (ScriptEngine)
            {
                return ScriptEngine.Evaluate(input);
            }
        }

        /// <summary>
        /// Executes the given string as JavaScript within the current environment.
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        /// <param name="script">The script to execute.</param>
        /// <param name="sourceName">The source of the script (such as a file name).</param>
        /// <param name="throwOnError">If true, any script errors should be thrown instead of returned.</param>
        [ScriptMember("execute")]
        public IBasicHandle Execute(string script, string sourceName = null, bool throwOnError = false)
        {
            lock (ScriptEngine)
            {
                return ScriptEngine.Execute(script, sourceName, throwOnError);
            }
        }

        /// <summary>
        /// Executes the given string as JavaScript within the current environment.
        /// <para>Note: This is a thread-safe call.</para>
        /// </summary>
        /// <param name="script">The script to execute.</param>
        /// <param name="sourceName">The source of the script (such as a file name).</param>
        /// <param name="throwOnError">If true, any script errors should be thrown instead of returned.</param>
        [ScriptMember("loadScript")]
        public IBasicHandle LoadScript(string script, string sourceName = null, bool throwOnError = false)
        {
            lock (ScriptEngine)
            {
                return ScriptEngine.LoadScript(script, sourceName, throwOnError);
            }
        }
    }
}
